<template>
  <div class="frozen-grid-container">
    <!-- 首行冻结区域 -->
    <div
      v-if="showHeaderRow"
      class="frozen-header-row"
      :style="{
        height: headerRowHeight + 'px',
        width: visibleContentWidth + 'px',
        left: headerColumnWidth + 'px',
      }"
    >
      <div
        class="header-row-content"
        :style="{
          transform: `translateX(${-scrollLeft}px)`,
          display: 'grid',
          gridTemplateColumns: `repeat(${displayCols}, ${itemWidth}px)`,
          gap: gap + 'px',
          padding: `0 ${padding}px`,
        }"
      >
        <div
          v-for="col in visibleCols"
          :key="`header-${col}`"
          class="header-cell"
          :style="{
            height: headerRowHeight + 'px',
            width: itemWidth + 'px',
          }"
        >
          <slot name="header" :col="col" :index="col"> 列 {{ col + 1 }} </slot>
        </div>
      </div>
    </div>

    <!-- 首列冻结区域 -->
    <div
      v-if="showHeaderColumn"
      class="frozen-header-column"
      :style="{
        width: headerColumnWidth + 'px',
        height: visibleContentHeight + 'px',
        top: headerRowHeight + 'px',
      }"
    >
      <div
        class="header-column-content"
        :style="{
          transform: `translateY(${-scrollTop}px)`,
          display: 'grid',
          gridTemplateRows: `repeat(${displayRows}, ${itemHeight}px)`,
          gap: gap + 'px',
          padding: `${padding}px 0`,
        }"
      >
        <div
          v-for="row in visibleRows"
          :key="`row-${row}`"
          class="header-cell"
          :style="{
            height: itemHeight + 'px',
            width: headerColumnWidth + 'px',
          }"
        >
          <slot name="rowHeader" :row="row" :index="row">
            {{ row + 1 }}
          </slot>
        </div>
      </div>
    </div>

    <!-- 主内容区域 -->
    <div
      class="main-content-container"
      :style="{
        height: containerHeight + 'px',
        width: containerWidth + 'px',
        marginTop: showHeaderRow ? headerRowHeight + 'px' : '0',
        marginLeft: showHeaderColumn ? headerColumnWidth + 'px' : '0',
      }"
      @scroll="handleScroll"
      ref="containerRef"
    >
      <div
        class="virtual-grid-content"
        :style="{
          height: totalHeight + 'px',
          width: totalWidth + 'px',
        }"
      >
        <div
          class="virtual-grid-items"
          :style="{
            transform: `translate(${offsetX}px, ${offsetY}px)`,
            display: 'grid',
            gridTemplateColumns: `repeat(${displayCols}, ${itemWidth}px)`,
            gridTemplateRows: `repeat(${displayRows}, ${itemHeight}px)`,
            gap: gap + 'px',
            padding: padding + 'px',
          }"
        >
          <div
            v-for="item in visibleItems"
            :key="item.id"
            class="virtual-grid-item"
            :style="{
              height: itemHeight + 'px',
              width: itemWidth + 'px',
            }"
          >
            <slot
              :item="item"
              :index="item._index"
              :row="item._row"
              :col="item._col"
            >
              {{ item.text }}
            </slot>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, computed } from "vue";

interface VirtualGridItem {
  id: number;
  text: string;
  [key: string]: any;
}

interface Props {
  items: VirtualGridItem[];
  itemHeight: number;
  itemWidth: number;
  containerHeight: number;
  containerWidth: number;
  columns: number;
  gap?: number;
  padding?: number;
  bufferSize?: number;
  // 新增的冻结相关属性
  showHeaderRow?: boolean;
  showHeaderColumn?: boolean;
  headerRowHeight?: number;
  headerColumnWidth?: number;
}

const props = withDefaults(defineProps<Props>(), {
  itemHeight: 200,
  itemWidth: 200,
  containerHeight: 400,
  containerWidth: 800,
  columns: 3,
  gap: 10,
  padding: 16,
  bufferSize: 2,
  showHeaderRow: true,
  showHeaderColumn: true,
  headerRowHeight: 50,
  headerColumnWidth: 80,
});

const containerRef = ref<HTMLElement>();
const scrollTop = ref(0);
const scrollLeft = ref(0);
let ticking = false;
let lastScrollTime = 0;
const SCROLL_THROTTLE_MS = 16; // 约60fps

// 计算总行数
const rows = computed(() => Math.ceil(props.items.length / props.columns));

// 计算总宽度（包含间距和边距）
const totalWidth = computed(
  () =>
    props.columns * props.itemWidth +
    (props.columns - 1) * props.gap +
    props.padding * 2
);

// 计算总高度（包含间距和边距）
const totalHeight = computed(
  () =>
    rows.value * props.itemHeight +
    (rows.value - 1) * props.gap +
    props.padding * 2
);

// 计算可见内容区域的宽度和高度
const visibleContentWidth = computed(() => {
  return props.containerWidth;
});

const visibleContentHeight = computed(() => {
  return props.containerHeight;
});

// 合并可见区域计算，减少计算属性数量
const visibleRange = computed(() => {
  const startRow = Math.floor(scrollTop.value / (props.itemHeight + props.gap));
  const endRow =
    startRow +
    Math.ceil(visibleContentHeight.value / (props.itemHeight + props.gap)) +
    props.bufferSize * 2;

  const startCol = Math.floor(scrollLeft.value / (props.itemWidth + props.gap));
  const endCol =
    startCol +
    Math.ceil(visibleContentWidth.value / (props.itemWidth + props.gap)) +
    props.bufferSize * 2;

  return {
    startRow: Math.max(0, startRow - props.bufferSize),
    endRow: Math.min(endRow, rows.value),
    startCol: Math.max(0, startCol - props.bufferSize),
    endCol: Math.min(endCol, props.columns),
    displayRows:
      Math.min(endRow, rows.value) - Math.max(0, startRow - props.bufferSize),
    displayCols:
      Math.min(endCol, props.columns) -
      Math.max(0, startCol - props.bufferSize),
  };
});

const visibleStartRow = computed(() => visibleRange.value.startRow);
const visibleEndRow = computed(() => visibleRange.value.endRow);
const visibleStartCol = computed(() => visibleRange.value.startCol);
const visibleEndCol = computed(() => visibleRange.value.endCol);
const displayRows = computed(() => visibleRange.value.displayRows);
const displayCols = computed(() => visibleRange.value.displayCols);

// 计算可见行和列的范围（用于首行首列渲染）
// 首行首列采用与主内容区域相同的虚拟滚动策略
const visibleRows = computed(() => {
  const result = [];
  for (let i = visibleStartRow.value; i < visibleEndRow.value; i++) {
    result.push(i);
  }
  return result;
});

const visibleCols = computed(() => {
  const result = [];
  for (let i = visibleStartCol.value; i < visibleEndCol.value; i++) {
    result.push(i);
  }
  return result;
});

// 计算可见项目（优化版本：直接计算索引范围，避免嵌套循环）
const visibleItems = computed(() => {
  const result: (VirtualGridItem & {
    _index: number;
    _row: number;
    _col: number;
  })[] = [];

  for (let row = visibleStartRow.value; row < visibleEndRow.value; row++) {
    for (let col = visibleStartCol.value; col < visibleEndCol.value; col++) {
      const index = row * props.columns + col;
      if (index < props.items.length) {
        result.push({
          ...props.items[index],
          _index: index,
          _row: row,
          _col: col,
        });
      }
    }
  }

  return result;
});

// 计算偏移量
const offsetX = computed(
  () => visibleStartCol.value * (props.itemWidth + props.gap)
);
const offsetY = computed(
  () => visibleStartRow.value * (props.itemHeight + props.gap)
);

// 优化的滚动处理（双重节流：requestAnimationFrame + 时间节流）
const handleScroll = (event: Event) => {
  const now = Date.now();

  if (!ticking && now - lastScrollTime >= SCROLL_THROTTLE_MS) {
    requestAnimationFrame(() => {
      const target = event.target as HTMLElement;
      scrollTop.value = target.scrollTop;
      scrollLeft.value = target.scrollLeft;
      lastScrollTime = now;
      ticking = false;
    });
    ticking = true;
  }
};

// 滚动到指定位置
const scrollTo = (index: number) => {
  if (containerRef.value) {
    const row = Math.floor(index / props.columns);
    const col = index % props.columns;
    const scrollTop = row * (props.itemHeight + props.gap);
    const scrollLeft = col * (props.itemWidth + props.gap);
    containerRef.value.scrollTop = scrollTop;
    containerRef.value.scrollLeft = scrollLeft;
  }
};

// 滚动到指定行列
const scrollToPosition = (row: number, col: number) => {
  if (containerRef.value) {
    const scrollTop = row * (props.itemHeight + props.gap);
    const scrollLeft = col * (props.itemWidth + props.gap);
    containerRef.value.scrollTop = scrollTop;
    containerRef.value.scrollLeft = scrollLeft;
  }
};

// 滚动到指定行
const scrollToRow = (rowIndex: number) => {
  if (containerRef.value) {
    const scrollTop = rowIndex * (props.itemHeight + props.gap);
    containerRef.value.scrollTop = scrollTop;
  }
};

// 滚动到指定列
const scrollToCol = (colIndex: number) => {
  if (containerRef.value) {
    const scrollLeft = colIndex * (props.itemWidth + props.gap);
    containerRef.value.scrollLeft = scrollLeft;
  }
};

// 获取当前可见的区域范围
const getVisibleRange = () => {
  return {
    rows: {
      start: visibleStartRow.value,
      end: visibleEndRow.value,
    },
    cols: {
      start: visibleStartCol.value,
      end: visibleEndCol.value,
    },
  };
};

// 获取当前滚动位置
const getScrollPosition = () => {
  return {
    scrollTop: scrollTop.value,
    scrollLeft: scrollLeft.value,
  };
};

// 暴露方法给父组件
defineExpose({
  scrollTo,
  scrollToPosition,
  scrollToRow,
  scrollToCol,
  getVisibleRange,
  getScrollPosition,
});
</script>

<style scoped>
.frozen-grid-container {
  position: relative;
  border: 1px solid #e0e0e0;
  border-radius: 8px;
  background: white;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  overflow: hidden;
}

/* 首行冻结样式 */
.frozen-header-row {
  position: absolute;
  top: 0;
  z-index: 10;
  background: #f8f9fa;
  border-bottom: 2px solid #dee2e6;
  overflow: hidden;
}

.header-row-content {
  position: absolute;
  top: 0;
  left: 0;
}

/* 首列冻结样式 */
.frozen-header-column {
  position: absolute;
  left: 0;
  z-index: 10;
  background: #f8f9fa;
  border-right: 2px solid #dee2e6;
  overflow: hidden;
}

.header-column-content {
  position: absolute;
  top: 0;
  left: 0;
}

/* 主内容区域样式 */
.main-content-container {
  border: 1px solid #e0e0e0;
  border-radius: 8px;
  overflow: auto;
  background: white;
  position: relative;
}

.virtual-grid-content {
  position: relative;
}

.virtual-grid-items {
  position: absolute;
  top: 0;
  left: 0;
}

.virtual-grid-item {
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid #f0f0f0;
  border-radius: 4px;
  font-size: 14px;
  color: #333;
  background: #fafafa;
  transition: all 0.2s;
  cursor: pointer;
  box-sizing: border-box;
}

.virtual-grid-item:hover {
  background: #f0f0f0;
  border-color: #d0d0d0;
  transform: translateY(-2px);
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}

/* 表头单元格样式 */
.header-cell {
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid #dee2e6;
  background: #f8f9fa;
  font-size: 14px;
  font-weight: 600;
  color: #495057;
  box-sizing: border-box;
  border-radius: 4px;
}

/* 自定义滚动条样式 */
.main-content-container::-webkit-scrollbar {
  width: 8px;
  height: 8px;
}

.main-content-container::-webkit-scrollbar-track {
  background: #f1f1f1;
  border-radius: 4px;
}

.main-content-container::-webkit-scrollbar-thumb {
  background: #c1c1c1;
  border-radius: 4px;
}

.main-content-container::-webkit-scrollbar-thumb:hover {
  background: #a8a8a8;
}

/* 响应式设计 */
@media (max-width: 768px) {
  .virtual-grid-items {
    grid-template-columns: repeat(2, 1fr) !important;
  }
}

@media (max-width: 480px) {
  .virtual-grid-items {
    grid-template-columns: 1fr !important;
  }
}
</style>
